package com.free.mini.gateway.filter;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import com.alibaba.fastjson.JSON;
import com.free.mini.common.base.ResultCode;
import com.free.mini.common.base.ResultData;
import com.free.mini.gateway.constants.GatewayConstant;
import com.free.mini.gateway.utils.PathWildcardMatchUtil;
import com.free.mini.gateway.vo.UserBaseVO;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;

/**
 * 登录校验
 * @author dongsufeng
 * @version 1.0.0
 * @date 2022年1月29日
 * @time 上午11:46:09
 */
@Slf4j
@Component
@Order(-1)
public class LoginCheckRequestFilter implements GlobalFilter {

    /**
     * 不需要登录的URL列表
     */
    @Value("${skip.login.url.list:admin/user/login}")
    private String skipLoginUrlList;

    @Value("${login.valid:false}")
    private Boolean loginValid;

    /**
     * 登陆有效期滚动续期窗口时间
     * 单位：秒
     * 默认：30天
     */
    @Value("${login.valid.period:2592000}")
    private int loginValidPeriod;

    private final Map<String, UserBaseVO> cache = new ConcurrentHashMap<>();

    @SuppressWarnings("rawtypes")
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        try {
            GatewayContext gatewayContext = (GatewayContext) exchange.getAttributes()
                    .get(GatewayConstant.CACHE_GATEWAY_CONTEXT);
            URI uri = gatewayContext.getURI();
            if (!loginValid || PathWildcardMatchUtil.wildcardMatch(uri.getPath(), skipLoginUrlList)){
                log.warn("path:{} skip login gateway request filter. loginValid:{}",uri.getPath(),loginValid);
                return chain.filter(exchange);
            }
            String accessToken = gatewayContext.getAccessToken();
            if (StringUtils.isBlank(accessToken)) {
                gatewayContext.setResult(new ResultData(ResultCode.EXISTING_ACCOUNT_LOGON));
                return response(gatewayContext);
            }
            //从缓存获取，如果失效删除，集群模式可换成分布式缓存
            UserBaseVO cacheUser = cache.get(accessToken);
            if (Objects.isNull(cacheUser) || (System.currentTimeMillis() - cacheUser.getExpires()) > loginValidPeriod) {
                gatewayContext.setLogin(false);
                cache.remove(accessToken);
                gatewayContext.setResult(new ResultData(ResultCode.EXISTING_ACCOUNT_LOGON));
                return response(gatewayContext);
            }
            //token续期
            cacheUser.setExpires(System.currentTimeMillis()+loginValidPeriod);
            cache.put(accessToken, cacheUser);
            gatewayContext.setLogin(true);
            gatewayContext.setAccount(cacheUser.getAccount());
            gatewayContext.setAccountType(String.valueOf(cacheUser.getAccountType()));
            gatewayContext.setMobile(cacheUser.getMobile());
            gatewayContext.setEmail(cacheUser.getEmail());
            gatewayContext.setUserId(String.valueOf(cacheUser.getId()));
            Map<String,Object> decryptObj = gatewayContext.getParams();
            if (decryptObj == null){
                decryptObj = new HashMap<>();
            }
            decryptObj.put(GatewayConstant.USER_ID, cacheUser.getId());
            decryptObj.put(GatewayConstant.ACCOUNT_TYPE_MOBILE, cacheUser.getMobile());
            decryptObj.put(GatewayConstant.ACCOUNT_TYPE_EMAIL, cacheUser.getEmail());
            decryptObj.put(GatewayConstant.ACCOUNT,cacheUser.getAccount());
            decryptObj.put(GatewayConstant.ACCOUNT_TYPE,cacheUser.getAccountType());
            gatewayContext.setParams(decryptObj);
            if (log.isDebugEnabled()) {
                log.debug("addLoginParams -> userBaseVo={}",cacheUser.toJSON());
            }
            gatewayContext.setAttributes(GatewayConstant.CACHE_GATEWAY_CONTEXT,gatewayContext);
            return chain.filter(exchange);
        } catch (Exception e) {
            log.error("token校验执行异常", e);
            throw e;
        }
    }
    /**
     * 返回结果
     */
    private Mono<Void> response(GatewayContext gatewayContext) {
        ServerHttpResponse response = gatewayContext.getServerHttpResponse();
        DataBuffer buffer = response.bufferFactory()
                .wrap(JSON.toJSONString(gatewayContext.getResult()).getBytes(StandardCharsets.UTF_8));
        response.setStatusCode(HttpStatus.OK);
        // 指定编码，否则在浏览器中会中文乱码
        response.getHeaders().add(GatewayConstant.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        return response.writeWith(Mono.just(buffer));
    }
}